home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / pascal / dgsay.exe / DGSAY.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1989-06-30  |  38.2 KB  |  1,072 lines

  1. {
  2.  ╔═════════════════════════════════════════════════════════════════════════╗
  3.  ║                                                                         ║
  4.  ║        TITLE :      DGSAY.TPU,  Version 8907.01                         ║
  5.  ║      PURPOSE :      Write formatted text to screen.                     ║
  6.  ║       AUTHOR :      David Gerrold, CompuServe ID:  70307,544            ║
  7.  ║  _____________________________________________________________________  ║
  8.  ║                                                                         ║
  9.  ║   Written in Turbo Pascal, Version 5.5,                                 ║
  10.  ║   with routines from Turbo Professional, Version 5.0.                   ║
  11.  ║                                                                         ║
  12.  ║   Turbo Pascal is a product of Borland International.                   ║
  13.  ║   Turbo Professional is a product of TurboPower Software                ║
  14.  ║  _____________________________________________________________________  ║
  15.  ║                                                                         ║
  16.  ║  This is not public domain software.  This is shareware.                ║
  17.  ║  This software is copyright 1989, by David Gerrold.                     ║
  18.  ║                                                                         ║
  19.  ║        The Brass Cannon Corporation                                     ║
  20.  ║        9420 Reseda Blvd., #804                                          ║
  21.  ║        Northridge, CA 91324-2932.                                       ║
  22.  ║                                                                         ║
  23.  ║  If you find this code useful, a donation of $25 is requested --        ║
  24.  ║  not to me, but to the AIDS Project Los Angeles.  Donations may         ║
  25.  ║  be forwarded via the Brass Cannon address.  Thank you.                 ║
  26.  ║                                                                         ║
  27.  ╚═════════════════════════════════════════════════════════════════════════╝
  28.                                                                             }
  29. { ========================================================================= }
  30. {  Compiler Directives :                                                    }
  31. { ========================================================================= }
  32.  
  33. {$R-}    {Range checking off}
  34. {$B+}    {Boolean complete evaluation on}
  35. {$S+}    {Stack checking on}
  36. {$I+}    {I/O checking on}
  37. {$N+,E+} {Simulate numeric coprocessor}
  38. {$M 65520,16384,655360} {Turbo 3 default stack and heap}
  39. {$V-}    {Variable range checking off}
  40.  
  41. { ========================================================================= }
  42. { ========================================================================= }
  43.  
  44. UNIT DGsay;
  45.  
  46. { ========================================================================= }
  47. INTERFACE
  48. { ========================================================================= }
  49.  
  50. USES
  51.   Dos,                                           { TP5.5 unit }
  52.   TpDos,                                         { Turbo Professional unit }
  53.   TpCrt,                                         { Turbo Professional unit }
  54.   TpString,                                      { Turbo Professional unit }
  55.   DgInit,                                        { Dg initialization }
  56.   DgStr;                                         { Dg string object }
  57.  
  58. { ========================================================================= }
  59.  
  60. TYPE
  61. {
  62.   The SayKrnl-Object is the kernel ancestor for Say-Ob (see below);
  63.   None of the methods in SayKrnlOb are intended to be directly called
  64.   by the user;  they are for the internal workings of Say-Ob and its
  65.   descendants.
  66.  
  67.   The variables Indent, Width, JustifyFlag, and NormalAttr,
  68.   should be accessed only by the methods in SayOb.  The variable
  69.   CurrentAttr is for the object's internal bookkeeping and should
  70.   not be tampered with at all.
  71.  
  72.   The Send and SendKrnl methods are virtual, so that a child object can
  73.   be spawned for writing directly to the printer or to a disk file.  The
  74.   next version of this unit will contain such descendant objects.
  75. }
  76.  
  77.   SayKrnlOb = Object (StrOb)
  78.     Indent       : byte;                         { left indent }
  79.     Width        : byte;                         { paragraph width }
  80.     JustifyFlag  : boolean;                      { right justify or not? }
  81.  
  82.     NormalAttr   : byte;                         { normal attribute }
  83.     CurrentAttr  : byte;                         { current attribute }
  84.  
  85.     Constructor Init;
  86.     Function    GetLineBreak  (CheckStr : string) : byte;
  87.     Function    Justify (Jstr : string) : string;
  88.     Function    WordWrap (Limit : byte) : string;
  89.     Procedure   SendKrnl (SendStr : string);  virtual;
  90.     Procedure   Send     (SendStr : string);  virtual;
  91.     Procedure   SayKrnl  (AddStr  : string);
  92.     end;
  93.  
  94. {
  95.   The Say-Object is a replacement for the WriteLn procedure.  Use Say
  96.   and SayLn instead of Write and WriteLn.  The difference is that Say
  97.   will automatically reformat consecutive lines of text.  You can set
  98.   a defined screen width and SayOb will format the text to that width.
  99.   You may also specify a left-indent).
  100.  
  101.   Use consecutive Say ('<text>') commands to output formatted text
  102.   to the screen.  Use a SayLn ('<text>') command to end the paragraph
  103.   and empty the SayOb buffer.  Two consecutive SayLn ('') commands will
  104.   end the paragraph and output a blank line to the screen;
  105.  
  106.   If there is no text in the SayOb buffer, you may use SayLn ('') to
  107.   produce a blank line on screen.
  108. }
  109.   SayOb = Object (SayKrnlOb)
  110.     Constructor Init;
  111.     Procedure   SetIndent (I : byte);
  112.     Procedure   SetWidth (W : byte);
  113.     Procedure   SetAttr (A : byte);              { set NormalAttr }
  114.     Procedure   SetParams (I, W, A : byte;
  115.                            Jflag : boolean);
  116.     Procedure   JustOn;
  117.     Procedure   JustOff;
  118.  
  119.     Function    AttrStr (SetStr : string;
  120.                          A : byte) : string;
  121.  
  122.     Procedure   SayLn   (AddStr  : string);
  123.     Procedure   Say     (AddStr  : string);
  124.     Procedure   SayPara (AddStr  : string);
  125.     Procedure   SayAttr (AddStr  : string;
  126.                             Attr : byte);
  127.   end;
  128.  
  129. VAR
  130.   Simon : SayOb;
  131.  
  132. CONST
  133.   TabStr = '     ';                              { standard para indent }
  134.  
  135. { ========================================================================= }
  136.  
  137. PROCEDURE SayDoc;                                { simultaneous doc/demo }
  138.  
  139. { ========================================================================= }
  140. IMPLEMENTATION
  141. { ========================================================================= }
  142.  
  143. CONSTRUCTOR SayKrnlOb.Init;
  144.  
  145. BEGIN
  146.   S      := '';
  147.   Indent := 5;
  148.   Width  := 70;
  149.   JustifyFlag := true;
  150.  
  151.   NormalAttr  := TextAttr;
  152.   CurrentAttr := TextAttr;
  153. END;
  154.  
  155. { ========================================================================= }
  156.  
  157. FUNCTION SayKrnlOb.GetLineBreak (CheckStr : string) : byte;
  158. {
  159.   Locates the place to break the string for Wordwrap, allowing for
  160.   imbedded control characters.  Also used by the Justify function to
  161.   check the length of the string to be justified.
  162. }
  163. VAR
  164.   Len  : byte absolute CheckStr;
  165.   Loop : byte;
  166.   Ctr  : byte;
  167.  
  168. BEGIN
  169.   Ctr := Width;                                  { break here }
  170.   Loop := 0;
  171.   Repeat
  172.     inc (Loop);                                  { count through str }
  173.     if CheckStr [Loop] = #0 then begin           { if attribute change }
  174.       inc (Ctr, 2);                              { count it }
  175.       inc (Loop);                                { step past it }
  176.       end;
  177.   Until
  178.     (Loop >= Ctr)
  179.       or
  180.     (Loop >= Len);                                { until end of str }
  181.  
  182.   GetLineBreak := Ctr;                            { return count }
  183. END;
  184.  
  185. { ========================================================================= }
  186.  
  187. FUNCTION SayKrnlOb.Justify (Jstr : string) : string;
  188. {
  189.   Returns a string internally padded with spaces so that length = limit.
  190. }
  191. VAR
  192.   Jlen      : byte absolute Jstr;
  193.  
  194.   Loop      : byte;
  195.   LineBreak : byte;
  196.  
  197.   SpaceCtr,
  198.   SpaceCtr2 : byte;
  199.   InsertCtr : byte;
  200.   AddNum    : byte;
  201.   StartPos  : byte;
  202.  
  203. CONST
  204.   FlipFlag : boolean = false;
  205.  
  206. BEGIN
  207.   LineBreak := GetLineBreak (Jstr);              { allow for ctrl-chars }
  208.   If Jlen < LineBreak then                       { if padding needed }
  209.     If JustifyFlag then begin                    { if justify flat is on }
  210.       StartPos := 5 *                            { start count at what pos }
  211.         ord ((Pos (TabStr, Jstr) > 0)            { does Jstr start with }
  212.         and (Pos (TabStr, Jstr) < 4));           { a new paragraph? }
  213.       SpaceCtr := 0;                             { zero out counter }
  214.       For Loop := StartPos to Jlen do            { loop through Jstr }
  215.         If Jstr [Loop] = ' ' then
  216.           inc (SpaceCtr);                        { count spaces }
  217.  
  218.       InsertCtr := 0;                            { how many to insert }
  219.       AddNum := LineBreak - Jlen;                { how many spaces to add }
  220.       SpaceCtr2 := 0;                            { count spaces again }
  221.       If FlipFlag then
  222.         begin
  223.         Loop := StartPos;
  224.         Repeat
  225.           inc (Loop);
  226.           If Jstr [Loop] = ' ' then begin
  227.             If (InsertCtr/AddNum < SpaceCtr2/SpaceCtr) then begin
  228.               Insert (' ', Jstr, Loop);
  229.               inc (InsertCtr);
  230.               inc (Loop);
  231.               end;
  232.             Inc (SpaceCtr2);                     { count spaces }
  233.             end;
  234.         Until
  235.           Loop >= Jlen;
  236.         end
  237.       else
  238.         begin
  239.         Loop := Jlen;
  240.         Repeat
  241.           dec (Loop);
  242.           If Jstr [Loop] = ' ' then begin
  243.             If (InsertCtr/AddNum < SpaceCtr2/SpaceCtr) then begin
  244.               Insert (' ', Jstr, Loop);
  245.               inc (InsertCtr);
  246.               end;
  247.             Inc (SpaceCtr2);                     { count spaces }
  248.             end;
  249.         Until
  250.           Loop <= StartPos;
  251.         end;
  252.  
  253.       FlipFlag := not FlipFlag;                  { next time, go other way }
  254.       If Jlen < LineBreak then Jstr := Justify (Jstr);
  255.       end;
  256.   Justify := Jstr;
  257. END;
  258.  
  259. { ========================================================================= }
  260.  
  261. FUNCTION SayKrnlOb.WordWrap (Limit : byte) : string;
  262. {
  263.   If S is greater than width, WordWrap will break S in two at the last
  264.   space possible, returning the first part of the string as the result
  265.   of the function and deleting it from S, which will contain only the
  266.   remainder.  Additional text can then be appended to S for another
  267.   go-round.
  268. }
  269. VAR
  270.   Loop : byte;
  271.   Len  : byte absolute S;
  272.  
  273. BEGIN
  274.   Loop := Limit;                                 { start at screen width }
  275.   While
  276.     (Loop > 0)                                   { while str > '' }
  277.       and                                        { and }
  278.     (
  279.     (S [Loop] <> ' ')                            { char not space }
  280.       or                                         { or }
  281.     (S [pred (Loop)] = #0)                       { pred char is attr flag }
  282.     )
  283.   do
  284.     dec (Loop);                                  { count backward }
  285.   If Loop = 0 then                               { if no space }
  286.     Loop := Limit                                { break at limit }
  287.   else
  288.     While
  289.       (S [Loop] = ' ')                           { if loop at space }
  290.         and
  291.       (S [pred (Loop)] <> #0)
  292.     do
  293.       dec (Loop);                                { decrement }
  294.   WordWrap := Justify (SubStr (1, Loop));        { return justified str }
  295.   S := SubStr (succ (Loop), Len);                { delete it from S }
  296.   TrimCh (' ');                                  { delete spaces }
  297. END;
  298.  
  299. { ========================================================================= }
  300.  
  301. PROCEDURE SayKrnlOb.SendKrnl (SendStr : string);
  302. {
  303.   The thingamabob that does the job for the thingamabob that does the job
  304.   for the thingamabob that does the job....
  305. }
  306. VAR
  307.   AttrPos : byte;
  308.   Slen    : byte absolute SendStr;
  309.  
  310. BEGIN
  311.   AttrPos := Pos (#0, SendStr);                  { are there attr changes? }
  312.  
  313.   If AttrPos = 0 then begin                      { if no attr changes }
  314.     FastWrite (SendStr,
  315.       WhereY, WhereX, CurrentAttr);              { fastwrite text }
  316.     WriteLn;                                     { go to next line }
  317.     end
  318.   else begin                                     { else: }
  319.     FastWrite (GetSubStr (SendStr, 1, pred (AttrPos)),
  320.                WhereY, WhereX, CurrentAttr);     { write the first text }
  321.     Gotoxy (WhereX + pred (AttrPos), WhereY);    { advance cursor }
  322.     CurrentAttr :=
  323.       ord (SendStr [succ (AttrPos)]);            { get new attr }
  324.     Delete (SendStr, 1, succ (AttrPos));         { discard first text }
  325.     SendKrnl (SendStr);                          { recursive part of send }
  326.     end;
  327. END;
  328.  
  329. { ========================================================================= }
  330.  
  331. PROCEDURE SayKrnlOb.Send (SendStr : string);
  332. {
  333.   The thingamabob that does the job for the thingamabob that does the job.
  334.  
  335.   Send puts the cursor at the indent, then calls SendKrnl, which is a
  336.   recursive procedure, to finish writing the text to the screen.
  337. }
  338. BEGIN
  339.   Gotoxy (Indent, WhereY);                     { left indent }
  340.   SendKrnl (SendStr);
  341. END;
  342.  
  343. { ========================================================================= }
  344.  
  345. PROCEDURE SayKrnlOb.SayKrnl (AddStr : string);
  346. {
  347.   The thingamabob that does the job.
  348. }
  349.  
  350. VAR
  351.   Len       : byte absolute S;
  352.   LineBreak : byte;
  353.   Loop      : byte;                              { trash variable }
  354.  
  355. BEGIN
  356.   AddStr := #0 + chr (NormalAttr) + AddStr;      { reset normal attr }
  357.   If Len = 0 then                                { if no S then }
  358.     S := AddStr                                  { start with AddStr }
  359.   else begin
  360.     Loop := Len;
  361.     If S [pred (loop)] = #0 then                 { if len = attr char }
  362.       dec (Loop, 2);                             { look back 2 }
  363.     if InTwoSpacePunctuation (S [Loop]) then     { last char ends sentence? }
  364.       Append ('  ' + AddStr)                     { add two spaces }
  365.     else
  366.       Append (' ' + AddStr);                     { else add only one space }
  367.     end;
  368.   TrimTrailCh (' ');                             { delete extra spaces }
  369.  
  370.   Repeat                                         { repeat }
  371.     LineBreak := GetLineBreak (S);               { find place to break }
  372.     If Len > LineBreak then                      { if S > width }
  373.       Send (WordWrap (LineBreak));               { send wordwrapped text }
  374.   Until
  375.     Len <= LineBreak;                            { until S too short }
  376. END;
  377.  
  378. { ========================================================================= }
  379.  
  380. CONSTRUCTOR SayOb.Init;
  381.  
  382. BEGIN
  383.   SayKrnlOb.Init;
  384. END;
  385.  
  386. { ========================================================================= }
  387.  
  388. PROCEDURE SayOb.SetIndent (I : byte);
  389.  
  390. BEGIN
  391.   Indent := I;
  392. END;
  393.  
  394. { ========================================================================= }
  395.  
  396. PROCEDURE SayOb.SetWidth (W : byte);
  397.  
  398. BEGIN
  399.   Width := W;
  400. END;
  401.  
  402. { ========================================================================= }
  403.  
  404. PROCEDURE SayOb.SetAttr (A : byte);
  405.  
  406. BEGIN
  407.   NormalAttr  := A;
  408. END;
  409.  
  410. { ========================================================================= }
  411.  
  412. PROCEDURE SayOb.SetParams (I, W, A : byte;  Jflag : boolean);
  413.  
  414. BEGIN
  415.   SetIndent (I);
  416.   SetWidth (W);
  417.   SetAttr (A);
  418.   JustifyFlag := JFlag;
  419. END;
  420.  
  421. { ========================================================================= }
  422.  
  423. PROCEDURE SayOb.JustOn;
  424. BEGIN
  425.   JustifyFlag := true;
  426. END;
  427.  
  428. { ========================================================================= }
  429.  
  430. PROCEDURE SayOb.JustOff;
  431. BEGIN
  432.   JustifyFlag := false;
  433. END;
  434.  
  435. { ========================================================================= }
  436.  
  437. FUNCTION SayOb.AttrStr (SetStr : string;  A : byte) : string;
  438. {
  439.   Surrounds a string with two codes, the first to change it to a new attr,
  440.   the second to return it to the NormalAttr (normal attr for object).
  441. }
  442. BEGIN
  443.   AttrStr :=  #0 + chr (A) + SetStr + #0 + chr (NormalAttr);
  444. END;
  445.  
  446. { ========================================================================= }
  447.  
  448. PROCEDURE SayOb.SayLn (AddStr : string);
  449. {
  450.   Forces an end to a displayed paragraph of text.
  451.   Calls SayKrnl ('<text>'), then writes last line of text
  452.   to screen and flushes S.
  453. }
  454.  
  455. VAR
  456.   SaveJustifyFlag : boolean;
  457.  
  458. BEGIN
  459.   SayKrnl (AddStr);                              { send formatted text }
  460.   SaveJustifyFlag := JustifyFlag;                { save justification }
  461.   JustifyFlag := false;                          { turn justification off }
  462.   Send (S);                                      { send last text }
  463.   S := '';                                       { empty buffer }
  464.   JustifyFlag := SaveJustifyFlag;                { restore justification }
  465. END;
  466.  
  467. { ========================================================================= }
  468.  
  469. PROCEDURE SayOb.Say (AddStr : string);
  470. {
  471.   Replaces the Write procedure.  Consecutive calls to Say will
  472.   display formatted text on screen.
  473. }
  474. BEGIN
  475.   If Pos ('@NEW', AddStr) > 0 then               { filter paragraph command }
  476.     Replace ('@NEW', TabStr);
  477.   If (Pos (TabStr, AddStr) > 0)                  { new paragraph? }
  478.        and
  479.      (Pos (TabStr, AddStr) < 4)
  480.   then
  481.     SayLn ('');                                  { finish old paragraph }
  482.  
  483.   SayKrnl (AddStr);                              { write it }
  484. END;
  485.  
  486. { ========================================================================= }
  487.  
  488. PROCEDURE SayOb.SayPara (AddStr : string);
  489. {
  490.   Starts a new paragraph.  If the user has inserted leading spaces in the
  491.   line, these are deleted and replaced with a standardized tab indent.
  492. }
  493. BEGIN
  494.   AddStr := TrimLeadChars (AddStr, ' ');         { remove extra spaces }
  495.   Say (TabStr + AddStr);                         { write it }
  496. END;
  497.  
  498. { ========================================================================= }
  499.  
  500. PROCEDURE SayOb.SayAttr (AddStr : string;  Attr : byte);
  501. {
  502.   Encodes text with new attribute, then sends it to be said.
  503. }
  504. BEGIN
  505.   Say (AttrStr (AddStr, Attr));
  506. END;
  507.  
  508. { ========================================================================= }
  509.  
  510. PROCEDURE SayDoc;
  511. {
  512.   Test routine for demonstrating Say-Ob.  Run the test program appended
  513.   to this unit.  It will call this procedure and demonstrate the Say
  514.   and SayLn methods.
  515. }
  516. CONST
  517.   HeadLine  : SayOb =
  518.     (Row    : 1;                                 { irrelevant to this ob }
  519.      Col    : 1;                                 { the compiler demands it }
  520.      S      : '';                                { the string }
  521.      Indent : 0;
  522.      Width  : 79;
  523.      JustifyFlag : true;
  524.      NormalAttr  : LightGreen;
  525.      CurrentAttr : LightGreen);
  526.  
  527.   NormText  : SayOb =
  528.     (Row    : 1;
  529.      Col    : 1;
  530.      S      : '';
  531.      Indent : 5;
  532.      Width  : 70;
  533.      JustifyFlag : true;
  534.      NormalAttr  : LightBlue;
  535.      CurrentAttr : LightBlue);
  536.  
  537.   IndentedText : SayOb =
  538.     (Row    : 1;
  539.      Col    : 1;
  540.      S      : '';
  541.      Indent : 10;
  542.      Width  : 60;
  543.      JustifyFlag : true;
  544.      NormalAttr  : LightGray;
  545.      CurrentAttr : LightGray);
  546.  
  547.    NameAttr    : byte = White;
  548.    CommandAttr : byte = Yellow;
  549.    VarAttr     : byte = LightCyan;
  550.    ItalicAttr  : byte = LightMagenta;
  551.  
  552. BEGIN
  553.   ClrScr;
  554.   HiddenCursor;
  555.   TextAttr := Yellow;
  556.   WriteLn ('DgSay demonstration begins here:');
  557.   WriteLn;
  558.  
  559.   With HeadLine do begin
  560.     Say ('This is the documentation for');
  561.     Say (AttrStr ('DgSay', NameAttr) + '.');
  562.     Say ('This is also a simultaneous demonstration of how');
  563.     SayAttr ('DgSay', NameAttr);
  564.     SayLn ('can be used to enhance the outputted text of a program.');
  565.     end;
  566.  
  567.   With NormText do begin
  568.     SayPara (AttrStr ('DgSay', NameAttr));
  569.     Say ('is a Turbo Pascal unit designed');
  570.     Say ('to simplify the task of outputting formatted text to the');
  571.     Say ('screen.');
  572.     Say ('The');
  573.     SayAttr ('DgSay', NameAttr);
  574.     Say ('unit contains an object called');
  575.     Say (AttrStr ('SayOb', NameAttr) + ', plus a declared instance');
  576.     Say ('of the object called ' + AttrStr ('Simon', VarAttr) + '.');
  577.     SayAttr ('Simon', VarAttr);
  578.     Say ('is automatically initialized within the unit and');
  579.     Say ('is ready to use in your own programs.');
  580.     Say ('Simply add');
  581.     SayAttr ('USES DgSay', CommandAttr);
  582.     SayLn ('to the beginning of your code.');
  583.  
  584.     SayPara ('You can use');
  585.     Say (AttrStr ('Simon', VarAttr) + ',');
  586.     Say ('or you can declare instances of your own, each with');
  587.     Say ('its own margins and attributes.');
  588.     Say ('Using the methods in the');
  589.     SayAttr ('SayOb', NameAttr);
  590.     Say ('object you can display formatted');
  591.     SayAttr ('(and optionally justified)', ItalicAttr);
  592.     Say ('blocks of text as easily as if you were writing a series of');
  593.     Say (AttrStr ('WriteLn (', CommandAttr) +
  594.          AttrStr ('<text>', VarAttr) +
  595.          AttrStr (')', CommandAttr));
  596.     Say ('commands.  You can also use different');
  597.     Say (AttrStr ('C', LightRed) +
  598.          AttrStr ('O', Yellow) +
  599.          AttrStr ('L', LightGreen) +
  600.          AttrStr ('O', LightCyan) +
  601.          AttrStr ('R', LightBlue) +
  602.          AttrStr ('S', LightMagenta));
  603.     SayLn ('to highlight individual words or whole blocks of text.');
  604.     SayLn ('');
  605.     end;
  606.     Pause;
  607.     ClrScr;
  608.  
  609.   With HeadLine do
  610.     SayLn ('How it works:');
  611.  
  612.   With NormText do begin
  613.     SayPara ('The');
  614.     SayAttr ('SayOb', NameAttr);
  615.     Say ('works with a simple one-string buffer.');
  616.     Say ('To send formatted text to the screen,');
  617.     Say ('you use consecutive calls of');
  618.     Say (AttrStr ('Simon.Say (', CommandAttr) +
  619.          AttrStr ('<text to output>', VarAttr) +
  620.          AttrStr (')', CommandAttr));
  621.     Say ('to fill the buffer.');
  622.     Say ('Each call to');
  623.     Say (AttrStr ('Say (', CommandAttr) +
  624.          AttrStr ('<text to output>', VarAttr) +
  625.          AttrStr (')', CommandAttr));
  626.     SayLn ('adds its text to the end of the buffer.');
  627.  
  628.  
  629.     SayPara ('Every time the buffer grows');
  630.     Say ('beyond the declared width of the text, a routine within the');
  631.     SayAttr ('SayOb', NameAttr);
  632.     Say ('object sends formatted lines of');
  633.     Say ('text to the screen, until the buffer');
  634.     Say ('size is once again less than the declared width.');
  635.     Say ('This usually leaves a small amount of text in the buffer.');
  636.     Say ('At the end of a block of text, you flush the buffer with a');
  637.     Say ('call to the');
  638.     Say (AttrStr ('SayLn (', CommandAttr) +
  639.          AttrStr ('<text to output>', VarAttr) +
  640.          AttrStr (')', CommandAttr));
  641.     SayLn ('method.');
  642.     SayLn ('');
  643.     end;
  644.  
  645.   With HeadLine do begin
  646.     Say ('You are invited to examine the source code');
  647.     SayLn ('of this demonstration to see how it''s done.');
  648.     SayLn ('');
  649.     end;
  650.  
  651.     Pause;
  652.     ClrScr;
  653.  
  654.   With HeadLine do
  655.     SayLn ('How to send formatted text to the screen:');
  656.  
  657.   With NormText do begin
  658.     SayPara ('The simplest way to send formatted text is with the');
  659.     Say (AttrStr ('Say (', CommandAttr) +
  660.          AttrStr ('<text>', VarAttr) +
  661.          AttrStr (')', CommandAttr));
  662.     Say ('method, where');
  663.     SayAttr ('<text>', VarAttr);
  664.     SayLn ('represents a string of text to be written.');
  665.  
  666.     SayPara ('Consecutive calls to the');
  667.     Say (AttrStr ('Say (', CommandAttr) +
  668.          AttrStr ('<text>', VarAttr) +
  669.          AttrStr (')', CommandAttr));
  670.     Say ('method will append');
  671.     Say ('each new piece of text to the end of the line buffer.');
  672.     Say ('Appropriate spacing is always maintained.  External');
  673.     Say ('spaces are trimmed from the text to be appended, then');
  674.     Say ('one space is inserted between the preceding text and');
  675.     Say ('the text to be appended;');
  676.     Say ('two spaces are inserted if the preceding text ends with a');
  677.     Say ('colon, period, exclamation point,');
  678.     SayLn ('semi-colon, or question mark.');
  679.  
  680.     SayPara ('Each time the line buffer grows beyond');
  681.     Say ('the declared width of the format,');
  682.     SayAttr ('Simon', VarAttr);
  683.     Say ('will output formatted lines of text until the buffer size');
  684.     SayLn ('is again below the declared width.');
  685.  
  686.     SayPara ('To end a paragraph, use the');
  687.     Say (AttrStr ('SayLn (', CommandAttr) +
  688.          AttrStr ('<text>', VarAttr) +
  689.          AttrStr (')', CommandAttr));
  690.     Say ('method.');
  691.     Say (AttrStr ('SayLn (', CommandAttr) +
  692.          AttrStr ('<text>', VarAttr) +
  693.          AttrStr (')', CommandAttr));
  694.     Say ('first calls the');
  695.     Say (AttrStr ('Say (', CommandAttr) +
  696.          AttrStr ('<text>', VarAttr) +
  697.          AttrStr (')', CommandAttr));
  698.     Say ('method to finish writing any formattable text');
  699.     Say ('to the screen, then it');
  700.     Say ('writes all the remaining text to the screen and empties');
  701.     Say ('the buffer.');
  702.     Say ('A call to');
  703.     Say (AttrStr ('SayLn (', Yellow) +
  704.          AttrStr ('<text>', LightCyan) +
  705.          AttrStr (')', Yellow));
  706.     SayLn ('always flushes the line buffer.');
  707.     SayLn ('');
  708.     end;
  709.  
  710.     Pause;
  711.     ClrScr;
  712.  
  713.   With HeadLine do
  714.     SayLn ('How to start a new paragraph:');
  715.  
  716.   With NormText do begin
  717.     SayPara ('The simplest way to start a new paragraph is with the');
  718.     Say (AttrStr ('SayPara (', Yellow) +
  719.          AttrStr ('<text>', LightCyan) +
  720.          AttrStr (')', Yellow));
  721.     Say ('method, where');
  722.     SayAttr ('<text>', LightCyan);
  723.     Say ('represents the first string of text in the paragraph.');
  724.     Say ('Follow this with as many calls to the');
  725.     Say (AttrStr ('Say (', Yellow) +
  726.          AttrStr ('<text>', LightCyan) +
  727.          AttrStr (')', Yellow));
  728.     SayLn ('method as are necessary to complete the paragraph.');
  729.     end;
  730.  
  731.   With IndentedText do begin
  732.     SayPara ('If you are writing consecutive paragraphs');
  733.     Say ('you do not need to call the');
  734.     Say (AttrStr ('SayLn (', CommandAttr) +
  735.          AttrStr ('<text>', VarAttr) +
  736.          AttrStr (')', CommandAttr));
  737.     Say ('method to end the paragraph.  Every time you call');
  738.     Say (AttrStr ('SayPara (', CommandAttr) +
  739.          AttrStr ('<text>', VarAttr) +
  740.          AttrStr (')', CommandAttr));
  741.     Say ('it first calls');
  742.     Say (AttrStr ('SayLn (', CommandAttr) +
  743.          AttrStr ('<text>', VarAttr) +
  744.          AttrStr (')', CommandAttr));
  745.     Say ('to flush all text still in the line buffer.');
  746.     Say ('If you do call the');
  747.     Say (AttrStr ('SayLn (', CommandAttr) +
  748.          AttrStr ('<text>', VarAttr) +
  749.          AttrStr (')', CommandAttr));
  750.     Say ('method, followed by a call to');
  751.     Say (AttrStr ('SayPara (', CommandAttr) +
  752.          AttrStr ('<text>', VarAttr) +
  753.          AttrStr (')', CommandAttr));
  754.     Say ('you will get a blank line between the two');
  755.     SayLn ('paragraphs.');
  756.     end;
  757.  
  758.   With NormText do begin
  759.     SayPara ('The');
  760.     SayAttr ('other', ItalicAttr);
  761.     Say ('way to start a paragraph is to put');
  762.     SayAttr ('five blank spaces', ItalicAttr);
  763.     Say ('at the beginning of a text string.  This will produce');
  764.     Say ('the exact same result as a call to the');
  765.     Say (AttrStr ('SayPara (', CommandAttr) +
  766.          AttrStr ('<text>', VarAttr) +
  767.          AttrStr (')', CommandAttr));
  768.     Say ('method.');
  769.     SayLn ('Do not use ' +
  770.          AttrStr ('^I', VarAttr) +
  771.          ', it will not be recognized as a paragraph indent.');
  772.     SayLn ('');
  773.     end;
  774.  
  775.     Pause;
  776.     ClrScr;
  777.  
  778.   With HeadLine do
  779.     SayLn ('How to set the margins and attributes:');
  780.  
  781.   With NormText do begin
  782.     SayPara ('The left indent on this paragraph');
  783.     Say ('has been set to 5, the format width');
  784.     Say ('has been set to 70.  This will produce a margin of 5');
  785.     SayLn ('on both the right and the left sides of the screen.');
  786.  
  787.     SayPara ('The best way to set margins and attributes is to');
  788.     Say ('declare an instance of');
  789.     SayAttr ('SayOb', NameAttr);
  790.     Say ('as a typed constant.');
  791.     SayLn ('(See the source code for examples.)');
  792.     SayPara ('If you wish to change the margins of an object after');
  793.     Say ('it has been initialized, you may use the');
  794.     Say (AttrStr ('SetIndent (', CommandAttr) +
  795.          AttrStr ('<Indent>', VarAttr) +
  796.          AttrStr (')', CommandAttr));
  797.     Say ('method, where');
  798.     SayAttr ('<Indent>', VarAttr);
  799.     Say ('represents the number of columns to indent, and the');
  800.     Say (AttrStr ('SetWidth (', CommandAttr) +
  801.          AttrStr ('<Width>', VarAttr) +
  802.          AttrStr (')', CommandAttr));
  803.     Say ('method, where');
  804.     SayAttr ('<Width>', LightCyan);
  805.     Say ('represents the formatted length of the text line.');
  806.     Say ('You can reset margins in the middle of a block of text;');
  807.     SayLn ('but the results may not always be predictable.');
  808.  
  809.     SayPara ('The attribute for text may be set with the');
  810.     Say (AttrStr ('SetAttr (', CommandAttr) +
  811.          AttrStr ('<Attr>', VarAttr) +
  812.          AttrStr (')', CommandAttr));
  813.     Say ('method, where');
  814.     SayAttr ('<Attr>', VarAttr);
  815.     Say ('represents the desired attribute.');
  816.     Say ('You may reset the attribute at any time within a block');
  817.     SayLn ('of text.');
  818.     SayLn ('');
  819.     end;
  820.  
  821.     Pause;
  822.     ClrScr;
  823.  
  824.   With HeadLine do
  825.     SayLn ('How to turn justification on and off:');
  826.  
  827.   With IndentedText do begin
  828.     JustOff;
  829.     SayPara ('Not everybody wants justified text.  Some people');
  830.     Say ('find it annoying.  You can switch text justification');
  831.     SayAttr ('on', ItalicAttr);
  832.     Say ('and');
  833.     SayAttr ('off', ItalicAttr);
  834.     Say ('with the');
  835.     SayAttr ('JustOn', CommandAttr);
  836.     Say ('and');
  837.     SayAttr ('JustOff', CommandAttr);
  838.     Say ('methods.');
  839.     Say ('With justification set to');
  840.     Say (AttrStr ('off', ItalicAttr) + ',');
  841.     Say ('the formatting routines will continue to format text from');
  842.     Say ('the line buffer;  but the lines will not be padded to meet');
  843.     Say ('the right margin precisely.  This paragraph is');
  844.     SayAttr ('not', ItalicAttr);
  845.     SayLn ('justified.');
  846.     JustOn;
  847.     end;
  848.  
  849.   With NormText do begin
  850.     SayPara ('You can also set all of the margin and attribute parameters');
  851.     Say ('at the same time with a call to the');
  852.     Say (AttrStr ('SetParams (', CommandAttr) +
  853.          AttrStr ('<Indent, Width, Attr, JustifyFlag>', LightCyan) +
  854.          AttrStr (')', CommandAttr));
  855.     Say ('method.');
  856.     Say ('Generally, you should only reset the margin parameters');
  857.     Say ('and the Justify flag between paragraphs, or');
  858.     Say ('when the text buffer is empty;  otherwise, the results could');
  859.     Say ('be unpredictable.  You may safely reset the text attribute');
  860.     SayLn ('at any time.');
  861.     SayLn ('');
  862.     end;
  863.  
  864.     Pause;
  865.     ClrScr;
  866.  
  867.   With HeadLine do
  868.     SayLn ('More about Attributes:');
  869.  
  870.   With NormText do begin
  871.     SayPara ('As you can see,');
  872.     SayAttr ('SayOb', NameAttr);
  873.     Say ('is also capable of formatting text in color.');
  874.     Say ('You can write text to the screen in any attribute');
  875.     SayLn ('you choose.');
  876.  
  877.     SayPara ('Examples:');
  878.     SayAttr ('Black on LightGray.', BlackLightGray);
  879.     Say (AttrStr ('White on Red', WhiteRed) +
  880.          AttrStr (' Yellow on Red.', YellowRed));
  881.     SayAttr ('LightCyan + blinking.', LightCyan + blinking);
  882.     SayAttr ('Light Green on Green.', LightGreenGreen);
  883.     Say (AttrStr ('Light Magenta.', LightMagenta));
  884.     Say (AttrStr (' White on Brow', WhiteBrown) +
  885.          AttrStr ('n and Green', WhiteGreen));
  886.     SayAttr ('Light Red + blinking on Magenta.', LightRedMagenta + blinking);
  887.     SayLn (AttrStr ('Or even a nice restful dark gray.', DarkGray));
  888.  
  889.     SayPara ('Attributes can be set in two ways.');
  890.     Say ('The easiest way is with a call to the');
  891.     Say (AttrStr ('SayAttr (', CommandAttr) +
  892.          AttrStr ('<Text, Attr>', VarAttr) +
  893.          AttrStr (')', CommandAttr));
  894.     Say ('method, where');
  895.     SayAttr ('Text', VarAttr);
  896.     Say ('is the string to be written, and');
  897.     SayAttr ('Attr', VarAttr);
  898.     SayLn ('is the attribute.');
  899.     SayPara ('In all other respects,');
  900.     Say (AttrStr ('SayAttr (', CommandAttr) +
  901.          AttrStr ('<Text, Attr>', VarAttr) +
  902.          AttrStr (')', CommandAttr));
  903.     Say ('works exactly like the');
  904.     Say (AttrStr ('Say (', CommandAttr) +
  905.          AttrStr ('<Text>', VarAttr) +
  906.          AttrStr (')', CommandAttr));
  907.     Say ('method.');
  908.     Say (AttrStr ('SayAttr (', CommandAttr) +
  909.          AttrStr ('<Text, Attr>', VarAttr) +
  910.          AttrStr (')', CommandAttr));
  911.     Say ('only writes its own text in the new attribute.  The normal');
  912.     Say ('attribute for the rest of the paragraph is not affected.');
  913.     Say ('The only disadvantage of using the');
  914.     Say (AttrStr ('SayAttr (', CommandAttr) +
  915.          AttrStr ('<Text, Attr>', VarAttr) +
  916.          AttrStr (')', CommandAttr));
  917.     Say ('method is that, like');
  918.     Say (AttrStr ('Say (', CommandAttr) +
  919.          AttrStr ('<Text>', VarAttr) +
  920.          AttrStr (')', CommandAttr));
  921.     Say ('it will add spaces around the text as it appends it to the');
  922.     SayLn ('line buffer.  There are times when you may not want to do this.');
  923.     SayLn ('');
  924.     end;
  925.  
  926.     Pause;
  927.     ClrScr;
  928.  
  929.   With HeadLine do begin
  930.     Say ('The');
  931.     SayAttr ('other', ItalicAttr);
  932.     SayLn ('way to set the attribute (cont''d):');
  933.     end;
  934.  
  935.   With NormText do begin
  936.     SayPara ('The');
  937.     Say ('other way to set an attribute is to call the');
  938.     Say (AttrStr ('AttrStr (', CommandAttr) +
  939.          AttrStr ('<Text, Attr>', VarAttr) +
  940.          AttrStr (')', CommandAttr));
  941.     Say ('function, inside a call to');
  942.     Say (AttrStr ('Say (', CommandAttr) +
  943.          AttrStr ('<Text>', VarAttr) +
  944.          AttrStr (')', CommandAttr));
  945.     Say ('or');
  946.     Say (AttrStr ('SayLn (', CommandAttr) +
  947.          AttrStr ('<Text>', VarAttr) +
  948.          AttrStr (')', CommandAttr));
  949.     Say ('or');
  950.     Say (AttrStr ('SayPara (', CommandAttr) +
  951.          AttrStr ('<Text>', VarAttr) +
  952.          AttrStr (')', CommandAttr));
  953.     Say ('or even');
  954.     SayLn (AttrStr ('SayAttr (', CommandAttr) +
  955.          AttrStr ('<Text, Attr>', VarAttr) +
  956.          AttrStr (')', CommandAttr) + '.');
  957.  
  958.     SayPara ('The');
  959.     Say (AttrStr ('AttrStr (', CommandAttr) +
  960.          AttrStr ('<Text, Attr>', VarAttr) +
  961.          AttrStr (')', CommandAttr));
  962.     Say ('function returns a string with the appropriate attribute codes');
  963.     Say ('already embedded in it.  This allows you to concatenate');
  964.     Say ('two or more color-coded strings for special effects such as');
  965.     Say (AttrStr ('S', WhiteRed + blinking) +
  966.          AttrStr ('P', WhiteBrown + blinking) +
  967.          AttrStr ('E', WhiteGreen + blinking) +
  968.          AttrStr ('C', WhiteCyan + blinking) +
  969.          AttrStr ('T', WhiteBlue + blinking) +
  970.          AttrStr ('R', WhiteMagenta + blinking) +
  971.          AttrStr ('A', WhiteLightGray + blinking) +
  972.          '.');
  973.     Say ('For example:');
  974.     Say (AttrStr ('Say (', CommandAttr) +
  975.          AttrStr ('<Text + AttrStr (<text, attr>) + text>', VarAttr) +
  976.          AttrStr (')', CommandAttr));
  977.     Say ('will let you put text of any color');
  978.     Say ('you want in the middle of a line, or even in the middle');
  979.     SayLn ('of a wo' + AttrStr ('r', ItalicAttr) + 'd.');
  980.  
  981.     SayPara ('A word of caution:  poor choices of colors,');
  982.     Say ('or too many colors on the screen at the same');
  983.     Say ('time, can be confusing or even annoying');
  984.     Say ('to the user.  This program has been a deliberate demonstration');
  985.     Say ('of the power of the');
  986.     SayAttr ('SayOb', NameAttr);
  987.     Say ('object, but');
  988.     Say ('the author is the first to acknowledge that');
  989.     Say ('excessive demonstrations do not necessarily');
  990.     SayLn ('represent the most attractive design.');
  991.     end;
  992.  
  993.   With HeadLine do begin
  994.     SayPara ('Examine the source code for this demonstration to see');
  995.     Say ('how best to use the');
  996.     Say (AttrStr ('AttrStr (', CommandAttr) +
  997.          AttrStr ('<Text, Attr>', VarAttr) +
  998.          AttrStr (')', CommandAttr));
  999.     SayLn ('method.');
  1000.     SayLn ('');
  1001.     end;
  1002.  
  1003.     Pause;
  1004.     ClrScr;
  1005.  
  1006.   With HeadLine do
  1007.     SayLn ('Conclusion:');
  1008.  
  1009.   With NormText do begin
  1010.     SayPara ('Thank you for taking the time to run this');
  1011.     Say ('demonstration.');
  1012.     Say ('I wrote this unit because I wanted a way to write');
  1013.     Say ('attractively formatted text to the screen that was as');
  1014.     Say ('easy as using the');
  1015.     Say (AttrStr ('WriteLn (', CommandAttr) +
  1016.          AttrStr ('<text>', VarAttr) +
  1017.          AttrStr (')', CommandAttr));
  1018.     Say ('command.  Your feedback will be appreciated.');
  1019.     Say ('The next version of this unit will include descendant objects');
  1020.     SayLn ('for writing formatted text to the printer and to diskfiles.');
  1021.     SayPara ('This unit was written under');
  1022.     Say (AttrStr ('Turbo Pascal, Version 5.5', NameAttr) + ';');
  1023.     Say ('and requires the three units from');
  1024.     SayAttr ('Turbo Professional, Version 5.0', NameAttr);
  1025.     SayLn ('for compilation.');
  1026.     end;
  1027.  
  1028.   With IndentedText do begin
  1029.     SetParams (10, 60, LightGreen, true);
  1030.     SayPara ('This program was written by David Gerrold,');
  1031.     Say ('CompuServe ID:  70307,544.');
  1032.     Say ('Copyright 1989, by David Gerrold and The Brass Cannon Corporation.');
  1033.     SayLn ('All rights not specifically granted in this license are reserved.');
  1034.     SayPara ('This software is');
  1035.     SayAttr ('not', Yellow);
  1036.     Say ('public domain.  It is shareware.  If you find this software');
  1037.     Say ('useful, you are requested to make a donation of $25 or more');
  1038.     Say ('to the AIDS Project, Los Angeles.  Donations may be forwarded');
  1039.     Say ('via The Brass Cannon Corp, 9420 Reseda Blvd., #804, Northridge,');
  1040.     SayLn ('CA 91328.');
  1041.     end;
  1042.  
  1043.     PauseWithPrompt ('Press any key to exit demo.');
  1044.  
  1045.     WriteLn;
  1046.     WriteLn;
  1047.     WriteLn ('This ends the demonstration.');
  1048.     WriteLn ('The normal text attribute has not been disturbed.');
  1049. END;
  1050.  
  1051. { ========================================================================= }
  1052. {  Initialization :                                                         }
  1053. { ========================================================================= }
  1054.  
  1055. BEGIN
  1056.   Simon.Init;
  1057. END.
  1058.  
  1059. { ========================================================================= }
  1060.  
  1061. {
  1062.   Extract and run the program below to see how SayOb works.
  1063. }
  1064.  
  1065. PROGRAM SayDemo;
  1066.  
  1067. USES DgSay;
  1068.  
  1069. BEGIN
  1070.   SayDoc;
  1071. END.
  1072.